//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ManipulateMol2File.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <iostream>
#include <fstream>

#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 50000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMol2File::ManipulateMol2File() {
  this->locationDBPDB      = new String();
  this->locationDBMOL2     = new String();
  this->locationOut        = new String();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMol2File::ManipulateMol2File(const ManipulateMol2File& manipulateMol2File) {
  this->locationDBPDB      = new String(manipulateMol2File.locationDBPDB);
  this->locationDBMOL2     = new String(manipulateMol2File.locationDBMOL2);
  this->locationOut        = new String(manipulateMol2File.locationOut);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMol2File::ManipulateMol2File(const ManipulateMol2File* manipulateMol2File) {
  this->locationDBPDB      = new String(manipulateMol2File->locationDBPDB);
  this->locationDBMOL2     = new String(manipulateMol2File->locationDBMOL2);
  this->locationOut        = new String(manipulateMol2File->locationOut);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ManipulateMol2File::~ManipulateMol2File() {
  if (this->locationDBPDB)  delete this->locationDBPDB;
  if (this->locationDBMOL2) delete this->locationDBMOL2;
  if (this->locationOut)    delete this->locationOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMol2File::LocationDBPDB(String* locationDBPDB) {

  if (locationDBPDB) {
    if (this->locationDBPDB) delete this->locationDBPDB;
    this->locationDBPDB = locationDBPDB;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMol2File::LocationDBMOL2(String* locationDBMOL2) {

  if (locationDBMOL2) {
    if (this->locationDBMOL2) delete this->locationDBMOL2;
    this->locationDBMOL2 = locationDBMOL2;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ManipulateMol2File::LocationOut(String* locationOut) {

  if (locationOut) {
    if (this->locationOut) delete this->locationOut;
    this->locationOut = locationOut;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMol2File::LocationDBPDB(void) {
  return this->locationDBPDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMol2File::LocationDBMOL2(void) {
  return this->locationDBMOL2;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ManipulateMol2File::LocationOut(void) {
  return this->locationOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMol2File::ExtractPDBCoordinatesFromPDBFile(void) {

  if (!this->locationDBPDB      || !this->locationDBPDB->Length()  ||
      !this->locationDBMOL2     || !this->locationDBMOL2->Length() ||
      !this->locationOut        || !this->locationOut->Length())
    return false;

  TListE <String> *listFiles = new TListE <String> ();

  struct dirent **namelist;
  int             n;

  n = scandir(this->locationDBMOL2->Out(), &namelist, 0, alphasort);

  if (n >= 0) {

    while(n--) {

      if (strcmp(namelist[n]->d_name, ".") && strcmp(namelist[n]->d_name, "..")) {
        String* nameFile = new String(namelist[n]->d_name);
        listFiles->Add(nameFile);
      }

      free(namelist[n]);

    }

    free(namelist);

  }

  char    buffer1[LENGTHLINE + 1], buffer2[LENGTHLINE + 1];
  char*   completePathPDB   = NULL;
  char*   completePathMOL2  = NULL;
  char*   completePathOut   = NULL;

  fstream fileMOL2, fileOut;
  gzFile  filePDB;

  cout << "Reading Files (" << listFiles->Length() << ")..." << endl;
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  listFiles->SetInitial();

  while (listFiles->SetNext()) {

    completePathPDB  = new char[strlen(this->locationDBPDB->Out())  + strlen(listFiles->GetCurrent()->Out())     +  30];
    completePathMOL2 = new char[strlen(this->locationDBMOL2->Out()) + strlen(listFiles->GetCurrent()->Out())     +  10];
    completePathOut  = new char[strlen(this->locationOut->Out())    + strlen(listFiles->GetCurrent()->Out())     +  10];

    String pdbID, chain;
    
    pdbID.In(listFiles->GetCurrent()->Out());
    chain.In(listFiles->GetCurrent()->Out());
    
    pdbID.Cut(0, 4);
    chain.Cut(5, 1);
    
    sprintf(completePathPDB,  "%spdb%s.ent.gz\0", this->locationDBPDB->Out(),  pdbID.Out());
    sprintf(completePathMOL2, "%s%s\0",           this->locationDBMOL2->Out(), listFiles->GetCurrent()->Out());
    sprintf(completePathOut,  "%s%s_%s.pdb\0",       this->locationOut->Out(),    pdbID.Out(), chain.Out());

    cout << "MOL2 File :" << completePathMOL2 << endl;
    cout << "PDB  File :" << completePathPDB  << endl;
    cout << "Out  File :" << completePathOut  << endl;
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

    filePDB = gzopen(completePathPDB, "rb9");
    
    fileMOL2.open(completePathMOL2, fstream::in);
    fileOut.open(completePathOut,   fstream::out);

    bool             conditionRead = false;
    String           coordX, coordY, coordZ;
    TListE <String>* column = new TListE <String> ();
    
    while (!fileMOL2.eof() || !gzeof(filePDB)) {
        
      fileMOL2.getline(buffer1, LENGTHLINE); 
      if (!conditionRead) {
        if (strstr(buffer1, "@<TRIPOS>ATOM")) 
          conditionRead = true;
        continue;
      }
      else if (strstr(buffer1, "@<TRIPOS>BOND")) break;
      
      column->Clear();
      this->SplitColumn(buffer1, column, " \t");
      
      coordX.In(column->Get(2)); coordX.Trim();
      coordY.In(column->Get(3)); coordY.Trim();
      coordZ.In(column->Get(4)); coordZ.Trim();
      
      while (!gzeof(filePDB)) {
        gzgets(filePDB, buffer2, LENGTHLINE);
        if (strstr(buffer2, coordX.Out()) && strstr(buffer2, coordY.Out()) && strstr(buffer2, coordZ.Out())) {
          fileOut << buffer2;
          cout << buffer2;
          break;  
        }      
      }
      
    }

    gzclose(filePDB);
    
    fileMOL2.close();
    fileOut.close();
    
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

    if (column)           delete   column;

    if (completePathPDB)  delete[] completePathPDB;
    if (completePathMOL2) delete[] completePathMOL2;
    if (completePathOut)  delete[] completePathOut;
    
  }

  if (listFiles) delete listFiles;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMol2File::ExtractExtendedPDBCoordinatesFromPDBFile(void) {

  if (!this->locationDBPDB      || !this->locationDBPDB->Length()  ||
      !this->locationDBMOL2     || !this->locationDBMOL2->Length() ||
      !this->locationOut        || !this->locationOut->Length())
    return false;

  TListE <String> *listFiles = new TListE <String> ();

  struct dirent **namelist;
  int             n;

  n = scandir(this->locationDBMOL2->Out(), &namelist, 0, alphasort);

  if (n >= 0) {

    while(n--) {

      if (strcmp(namelist[n]->d_name, ".") && strcmp(namelist[n]->d_name, "..")) {
        String* nameFile = new String(namelist[n]->d_name);
        listFiles->Add(nameFile);
      }

      free(namelist[n]);

    }

    free(namelist);

  }

  char    buffer1[LENGTHLINE + 1], buffer2[LENGTHLINE + 1], buffer3[LENGTHLINE + 1];
  char*   completePathPDB   = NULL;
  char*   completePathMOL2  = NULL;
  char*   completePathOut   = NULL;

  fstream fileMOL2, fileOut;
  gzFile  filePDB, filePDBExtended;

  cout << "Reading Files (" << listFiles->Length() << ")..." << endl;
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  listFiles->SetInitial();

  while (listFiles->SetNext()) {

    completePathPDB  = new char[strlen(this->locationDBPDB->Out())  + strlen(listFiles->GetCurrent()->Out())     +  30];
    completePathMOL2 = new char[strlen(this->locationDBMOL2->Out()) + strlen(listFiles->GetCurrent()->Out())     +  10];
    completePathOut  = new char[strlen(this->locationOut->Out())    + strlen(listFiles->GetCurrent()->Out())     +  10];

    String pdbID, chain;
    
    pdbID.In(listFiles->GetCurrent()->Out());
    chain.In(listFiles->GetCurrent()->Out());
    
    pdbID.Cut(0, 4);
    chain.Cut(5, 1);
    
    sprintf(completePathPDB,  "%spdb%s.ent.gz\0", this->locationDBPDB->Out(),  pdbID.Out());
    sprintf(completePathMOL2, "%s%s\0",           this->locationDBMOL2->Out(), listFiles->GetCurrent()->Out());
    sprintf(completePathOut,  "%s%s_%s.pdb\0",       this->locationOut->Out(),    pdbID.Out(), chain.Out());

    cout << "MOL2 File :" << completePathMOL2 << endl;
    cout << "PDB  File :" << completePathPDB  << endl;
    cout << "Out  File :" << completePathOut  << endl;
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

    fileMOL2.open(completePathMOL2, fstream::in);
    fileOut.open(completePathOut,   fstream::out);

    bool             conditionRead = false;

    TListE <String>* column        = new TListE <String> ();
    TListE <String>* columnT       = new TListE <String> ();
    
    String           coordX, coordY, coordZ;

    while (!fileMOL2.eof() || !gzeof(filePDB)) {
        
      fileMOL2.getline(buffer1, LENGTHLINE); 
      if (!conditionRead) {
        if (strstr(buffer1, "@<TRIPOS>ATOM")) 
          conditionRead = true;
        continue;
      }
      else if (strstr(buffer1, "@<TRIPOS>BOND")) break;
      
      column->Clear();
      this->SplitColumn(buffer1, column, " \t");
      
      coordX.In(column->Get(2)); coordX.Trim();
      coordY.In(column->Get(3)); coordY.Trim();
      coordZ.In(column->Get(4)); coordZ.Trim();
      
      filePDB  = gzopen(completePathPDB, "rb9");
        
      while (!gzeof(filePDB)) {
          
        gzgets(filePDB, buffer2, LENGTHLINE);
        
        if (strstr(buffer2, coordX.Out()) && strstr(buffer2, coordY.Out()) && strstr(buffer2, coordZ.Out())) {

          String  line; 
          line.In(buffer2);
          
          String* residue       = line.SubStr(17, 20); 
          String* chainPDB      = line.SubStr(21, 22); 
          String* residueNumber = line.SubStr(22, 26); 
          
          residue->Trim();
          chainPDB->Trim();
          residueNumber->Trim();

          bool    findResidue   = false;
          
          long position = gztell(filePDB);
          
          filePDBExtended = gzopen(completePathPDB, "rb9");
          while (!gzeof(filePDBExtended)) {
            gzgets(filePDBExtended, buffer3, LENGTHLINE);
            if (strncmp(buffer3, "ATOM", 4) != 0) continue;
            findResidue = (!strncmp(buffer3 + 17, residue->Out(),       3))?true:false;
            findResidue = (!strncmp(buffer3 + 21, chainPDB->Out(),      1))?findResidue:false;
            findResidue = (!strncmp(buffer3 + 22, residueNumber->Out(), 4))?findResidue:false;
            if (findResidue) {
              cout    << buffer3;
              fileOut << buffer3;
            }
          }          
          gzclose(filePDBExtended);
          
          if (chainPDB)      delete chainPDB;
          if (residue)       delete residue;
          if (residueNumber) delete residueNumber;
          
          gzseek(filePDB, position, SEEK_SET);
          
        }      
      }
      
      gzclose(filePDB);

    }

        
    fileMOL2.close();
    fileOut.close();
    
    cout << "---------------------------------------------------------------------------------------------------------------------------------------------" << endl;

    if (column)           delete   column;
    if (columnT)          delete   columnT;

    if (completePathPDB)  delete[] completePathPDB;
    if (completePathMOL2) delete[] completePathMOL2;
    if (completePathOut)  delete[] completePathOut;
    
  }

  if (listFiles) delete listFiles;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ManipulateMol2File::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
